package edu.fiu.cis.personalize.server.berkDB;

import java.util.HashMap;
import java.util.Map;

import com.sleepycat.bind.tuple.TupleBinding;

import edu.fiu.cis.personalize.server.GraphMediator;
import edu.fiu.cis.personalize.server.berkDB.serializers.DocumentSerializer;
import edu.fiu.cis.personalize.server.berkDB.serializers.GroupSerializer;
import edu.fiu.cis.personalize.server.berkDB.serializers.IntegerSerializer;
import edu.fiu.cis.personalize.server.berkDB.serializers.QuerySerializer;
import edu.fiu.cis.personalize.server.berkDB.serializers.StringSerializer;
import edu.fiu.cis.personalize.server.berkDB.serializers.UserSerializer;
import edu.fiu.cis.personalize.server.graph.Document;
import edu.fiu.cis.personalize.server.graph.Group;
import edu.fiu.cis.personalize.server.graph.Node;
import edu.fiu.cis.personalize.server.graph.NodeType;
import edu.fiu.cis.personalize.server.graph.Query;
import edu.fiu.cis.personalize.server.graph.User;

public final class NodeStoreSerializer {

	private static BerkeleyDBTransaction<String, Integer> keyDB = new BerkeleyDBTransaction<String, Integer>(
			"key", new StringSerializer(), new IntegerSerializer());

	private static Map<NodeType, NodeTransaction<? extends Node>> fMap = new HashMap<NodeType, NodeStoreSerializer.NodeTransaction<? extends Node>>();

	static {
		BerkeleyDBConfigure.init(GraphMediator.CLEAR_DB_ON_LOAD);
		
		fMap.put(NodeType.DOCUMENT, new NodeTransaction<Document>() {

			@Override
			protected TupleBinding<Document> nodeSerializer() {
				return new DocumentSerializer();
			}

			@Override
			protected String dbName() {
				return "documents";
			}
		});

		fMap.put(NodeType.GROUP, new NodeTransaction<Group>() {

			@Override
			protected TupleBinding<Group> nodeSerializer() {
				return new GroupSerializer();
			}

			@Override
			protected String dbName() {
				return "groups";
			}
		});

		fMap.put(NodeType.USER, new NodeTransaction<User>() {

			@Override
			protected TupleBinding<User> nodeSerializer() {
				return new UserSerializer();
			}

			@Override
			protected String dbName() {
				return "users";
			}
		});

		fMap.put(NodeType.QUERY, new NodeTransaction<Query>() {
			@Override
			protected TupleBinding<Query> nodeSerializer() {
				return new QuerySerializer();
			}

			@Override
			protected String dbName() {
				return "queries";
			}
		});
	}

	public static void store(Node n) {
		fMap.get(n.getType()).store(n);
	}
	
	public static Node load(NodeType type,int id){
		return fMap.get(type).load(id);
	}

	public static void storeKeys(int u, int g, int q, int d) {
		keyDB.put("u", u);
		keyDB.put("g", g);
		keyDB.put("q", q);
		keyDB.put("d", d);

	}

	public static void destroy(boolean deleteDB) {
		if (deleteDB) {
			BerkeleyDBConfigure.closeAndClear();
		} else {
			BerkeleyDBConfigure.close();
		}
	}

	public static Map<String, Integer> loadKeys() {
		Map<String, Integer> m = new HashMap<String, Integer>();
		for (String k : new String[] { "u", "q", "d", "g" }) {
			Integer i = keyDB.get(k);
			m.put(k, i==null?0:i);
		}
		return m;
	}

	static abstract class NodeTransaction<E extends Node> {

		private BerkeleyDBTransaction<Integer, E> db = new BerkeleyDBTransaction<Integer, E>(
				dbName(), new IntegerSerializer(), nodeSerializer());

		public void _store(E n) {
			db.put(n.getId(), n);
		}

		@SuppressWarnings("unchecked")
		public void store(Node n) {
			_store((E) n);

		}
		
		public E load(int id){
			return db.get(id);
		}
		
		protected abstract TupleBinding<E> nodeSerializer();

		protected abstract String dbName();

	}

}
